package com.funromania.server.security.shiro;

import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAccount;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;

/**
 * User: deluan Date: Sep 21, 2010 Time: 9:32:45 PM
 */
public class DatastoreRealm extends AuthorizingRealm {

	static final String DEFAULT_USER_STORE_KIND = "ShiroUsers";

	static final Logger log = Logger
			.getLogger("com.deluan.shiro.gae.realm.DatastoreRealm");
	private final DatastoreService datastoreService;
	private String userStoreKind = DEFAULT_USER_STORE_KIND;

	public DatastoreRealm() {
		log.info("Creating a new instance of DatastoreRealm");
		this.datastoreService = DatastoreServiceFactory.getDatastoreService();
	}

	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(
			AuthenticationToken token) throws AuthenticationException {
		String username = ((UsernamePasswordToken) token).getUsername();
		log.info("Attempting to authenticate " + username + " in DB realm...");

		// Null username is invalid
		if (username == null) {
			throw new AccountException(
					"Null usernames are not allowed by this realm.");
		}

		// Get the user with the given username. If the user is not
		// found, then they don't have an account and we throw an
		// exception.
		Entity user = findByUsername(username);
		if (user == null) {
			throw new UnknownAccountException("No account found for user '"
					+ username + "'");
		}

		log.info("Found user " + username + " in DB");

		SimpleAccount account = new SimpleAccount(username,
				user.getProperty("passwordHash"), "DatastoreRealm");

		return account;
	}

	private Entity findByUsername(String username) {
		Query query = new Query(userStoreKind);
		query.addFilter("username", Query.FilterOperator.EQUAL, username);
		PreparedQuery preparedQuery = datastoreService.prepare(query);
		return preparedQuery.asSingleEntity();
	}

	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(
			PrincipalCollection principalCollection) {
		String username = (String) principalCollection.getPrimaryPrincipal();

		// Find the thing that stores your user's roles.
		Entity user = findByUsername(username);
		if (user == null) {
			if (log.isLoggable(Level.FINE)) {
				log.fine("Principal not found for authorizing user with username: "
						+ username);
			}
			return null;
		} else {
			if (log.isLoggable(Level.FINE)) {
				// log.fine(String.format("Authoriziong user %s with roles: %s",
				// username, user.getRoles()));
			}
			// SimpleAuthorizationInfo result = new SimpleAuthorizationInfo(
			// principal.getRoles());
			SimpleAuthorizationInfo result = new SimpleAuthorizationInfo(
					new HashSet<String>());
			return result;
		}
	}

	public void setUserStoreKind(String userStoreKind) {
		this.userStoreKind = userStoreKind;
	}

}
